package edpool;

public class Balancer<T>
{
    final private ToggleBit producerToggle, consumerToggle;
    final private LockFreeExchanger<T>[] exchangerArray;
    private ThreadLocal<Integer> lastSlotRange = new ThreadLocal<Integer>()
    		{
     	  		protected Integer initialValue() 
     	  		{ return 1; }
     	  	};
    public Balancer(int capacity, int level)
	{    	 
     	 exchangerArray = new LockFreeExchanger[capacity];
         for (int i = 0; i < capacity; i++)
             exchangerArray[i] = new LockFreeExchanger<T>();


    	producerToggle = new ToggleBit();
	    consumerToggle = new ToggleBit();

	}
     public void traverseBalancer(ExchangerPackage<T> myPackage) 
      {
    	 myPackage.setEliminationState(EliminationStateEnum.None);

    	 if (!myPackage.isDiffractOnly())
    	 {
    		 visit(myPackage);
	
    	 }
    	 
    	 
    	 if (myPackage.getEliminationState() == EliminationStateEnum.None)
    	 {
	   			 myPackage.setDirection((myPackage.getPackageType() == PackageTypeEnum.Producer) ? 
    					 				(producerToggle.traverse() ? 0:1) : (consumerToggle.traverse()? 0:1));
    		 
	   	 }  		 

      }
     
     
     public void visit(ExchangerPackage<T> myPackage)
     {   
     	 int slotRange = lastSlotRange.get(); 
         int maxLength = exchangerArray.length;

         int leftColisionTrials = Constants.maxColisionTrials;
         int leftTimeOuts = Constants.maxTimeOuts;
         int timeOutSpins = Constants.maxTimeOutSpins;
         
     	 ExchangeStatus status = ExchangeStatus.Success;
     	 try
     	 {
	         int slot;
	         while (true)
	         {
	        	
	         	if (slotRange == 1)
	         		slot = 0;
	         	else
	         		slot = MyRandom.Next(slotRange - 1, myPackage.getThreadId());
	         	
	 	
	         	status = exchangerArray[slot].exchange(myPackage, timeOutSpins);
	 			
	 	        if (status == ExchangeStatus.Failure)
	 	        { 
	           		slotRange = Math.min(maxLength, slotRange * 2);
		 	        leftColisionTrials--;
		 	        
	 	        }
	 	        
	 	        if (status == ExchangeStatus.Timeout)
	 	        {
	 	           	slotRange = Math.max(1, slotRange / 2);
		           	leftTimeOuts--;
		           	
		 	        if (leftTimeOuts <= Constants.maxTimeOuts * 3 / 4 && maxLength > 2)
		 	        {
		 	        	slotRange = 2;
		 	        	maxLength = 2;
		 	        }


		 	        if (leftTimeOuts <= Constants.maxTimeOuts / 2 && maxLength > 1)
		 	        {
		 	        	slotRange = 1;
		 	        	maxLength = 1;
		 	        	leftColisionTrials = Constants.maxColisionTrials * 3;
		 	        	myPackage.setDiffractOnly(true);
		 	        	timeOutSpins = Constants.maxTimeOutSpins * 2;

		 	        }	             		
		           	
	 	        }
		    	   
	 	        if (status == ExchangeStatus.Success || leftColisionTrials == 0 || leftTimeOuts == 0)
	 	        	return;
	 		}
     	 
     	 }
     	 finally
     	 {
     		 lastSlotRange.set(slotRange);
     		
     		 
       	 }
     }

}
